# -*-coding utf-8 -*-

__project__ = 'le-bo16'
__file_name__ = 'class14'
__author__ = 'hqDu'
__time__ = '2020-11-27 22:31'
__product_name = 'PyCharm'
# @fileName :class14.py

"""
__new__方法

1.__new__至少要有一个参数cls，代表要实例化的类，此参数在实例化时由Python解释器自动提供

2.__new__必须要有返回值，返回实例化出来的实例，这点在自己实现__new__时要特别注意，
可以return父类__new__出来的实例，或者直接是object的__new__出来的实例

3.__init__有一个参数self，就是这个__new__返回的实例，
__init__在__new__的基础上可以完成一些其它初始化的动作，__init__不需要返回值
"""

# class Person(object):
#
#     # 在魔法方法__init__ __str__ __del__之前还有一个魔法方法
#     # __new__用于创建对象，生成对象；
#     def __new__(cls, *args, **kwargs):
#         print(id(cls))  # cls就是class，表示要实例化的类Person，与print(id(Person))一致
#         print('new')
#         # pass  # __new__不会创造对象
#         ret = object.__new__(cls)  # 通过类创造对象，委托父类object帮我们创建对象，然后返回给子类Person
#         print(id(ret))
#         return ret
#
#     # 为对象添加属性，初始化属性，不能加返回值
#     def __init__(self):
#         print(id(self))  # 魔法方法__new__返回的实例就是self
#         print('init')
#         self.name = 'lebo'
#
#     # 追踪属性信息变化，返回字符串
#     def __str__(self):
#         return "名字：%s" % self.name
#
#     # 监听对象是否被销毁（对象的引用计数为0），销毁会调用__del__
#     def __del__(self):
#         print('del')
#
# print(id(Person))
# lebo = Person()  # 调用魔法方法__init__
# # print(lebo)  # 调用魔法方法__str__
# # 程序结束后，对象的引用计数为0，调用__del__

# new
# None



"""
单例模式：一个类只有一个对象的模式，就是单例模式。可以创建很多对象，但是第2个之后创建的对象还是第一个对象

一种对象创建型模式

用途：回收站（所有进入的的东西都需要标记为垃圾）、购物车（）；所有东西都回归到一个东西，每次记录的都是同一个值，就可以使用单例类进行记录

特点1：无论创建多少个对象，这些对象的十六进制内存地址是一样的，if not cls.instance
      单例类只能有一个对象，
      保证只有1个对象

特点2：创建多个对象，无论这些创建对象的时候，传参是否一样，都取第一次创建对象时传递的参数.if Person.flag:
        一个对象只能赋予一次属性，即使其他对象也赋予不同的属性值，也只能取第一次的属性值
        只执行1次__init__方法，只对属性进行一次初始化
        
减少没有必要的内存消耗
"""

# class Person(object):
#     # 让类拥有对象，所以需要添加类属性，是对类进行操作
#     # __init__，是让对象拥有属性，所以在__init__内添加对象的属性，是针对对象进行的操作
#     # None：特殊常量，表示值，不是0，不是False，不是空字符串（null）
#     # None表示值为空，表示空的对象没有进行赋值；not None表示没有值

#     # print(not None)返回True
#     # print(None)返回None
#     __instance = None  # 下划线，保证安全
#
#     # 创建多个对象，无论这些创建对象的时候，传参是否一样，都取第一次创建对象时传递的参数
#     __flag = True
#
#     # 在魔法方法__init__ __str__ __del__之前还有一个魔法方法
#     # __new__用于创建对象，生成对象；
#     def __new__(cls, *args, **kwargs):
#         """
#         单例模式：无论创建几个对象，print(对象名)后，这些对象的十六进制地址都是一样的，这样才是单例模式
#
#         if not None 与 if not cls.instance区别：
#         当针对类，创建两个对象后，
#         if not None 是对None进行判断，这样操作后，直接对两个对象操作：print(对象名)
#         发现，这两个十六进制的地址是不一致的
#
#         if not cls.instance，是对类属性进行一个判断；
#         创建多个对象，print(对象名)后，对象的十六进制地址都是一样的
#         第一次创建对象后，其他的对象可以理解为第一次创建的对象的引用
#
#         """
#         # if not None:
#         if not cls.__instance:  # 表示没有值（True?），表示没有对象，需要创建 if cls.instance is None
#             # 通过类创造对象，委托父类object帮我们创建对象，然后返回给子类Person
#             # 父类创建的对象，赋值给类属性
#             cls.__instance =  object.__new__(cls)
#         # 类对象有值，就直接返回类对象
#         return  cls.__instance
#
#
#     # 为对象添加属性，初始化属性，不能加返回值
#     def __init__(self,name):
#         print('init')
#         # 给对象赋值了True，没有给对象赋值False
#         # ok
#         if Person.__flag:
#             self.name = name
#             Person.__flag = False
#         # ok
#         # if self.flag:
#         #     self.name = name
#         #     self.flag = False
#         # 不ok
#         # if Person.flag:
#         #     self.name = name
#         #     self.flag = False
#         # OK
#         # if self.flag:
#         #     self.name = name
#         #     Person.flag = False
#
#     # 追踪属性信息变化，返回字符串
#     # 注释掉该方法，那么print(对象名)就会打印对象的十六进制地址
#     def __str__(self):
#         return "名字：%s" % self.name
#
#     # 监听对象是否被销毁（对象的引用计数为0），销毁会调用__del__
#     def __del__(self):
#         print('del')
#
#
# lebo = Person('le')
# print(lebo)
# print('-'*50)
# lebo1 = Person('bo')
# print(lebo1)

"""
异常

捕获异常，提高用户的体验
"""


# 报错异常类型：异常描述信息
# FileNotFoundError: [Errno 2] No such file or directory: 'lebo.txt'
# try...expect...是配套的

try:
    open('lebo.txt','r')
except FileNotFoundError:
    print("这有个问题下次处理")

# 当不确定是什么类型的错误时，expect后可以不写异常类型
try:
    open('lebo.txt','r')
except:
    print("这有个问题下次处理")

# 当代码有错误，但是与expect后写的异常类型不符的时候，不会捕获异常
try:
    open(a)
except FileNotFoundError:
    print("这有个问题下次处理")

# 捕获多个异常类型
try:
    open(a)
except (FileNotFoundError, NameError):
    print("这有个问题下次处理")

# 因为第一个expect中的NameError可以捕获到异常，就不会继续执行后续的expect，不会打印haha
try:
    open(a)
except (FileNotFoundError, NameError):
    print("这有个问题下次处理")
except:
    print('haha')

# 因为第一个expect中的不可以捕获到异常，所以会继续执行后续的expect，打印haha
try:
    open(a)
except FileNotFoundError:
    print("这有个问题下次处理")
except:
    print('haha')



# 存储（打印）异常的描述信息 as 变量
try:
    open('lebo.txt','r')
except FileNotFoundError  as result:  # result，是变量，用于存储异常的基本信息
    print("这有个问题", result)

# 不知道异常类型，打印异常描述信息，异常类型：写父类异常类型Exception/BaseException
# object不可以，因为里面没有异常的内容；写有异常的最基础的类就是BaseException
try:
    open('lebo.txt','r')
except Exception  as result:  # result，是变量，用于存储异常的基本信息
    print("这有个问题", result)

# finally 无论是否能够捕获异常，都会执行finally
try:
    open('lebo.txt','r')
except Exception  as result:  # result，是变量，用于存储异常的基本信息
    print("这有个问题", result)
finally:
    print('finally')

# else，尝试执行代码，如果正确就会执行else；如果错误，就会进入except内，捕获异常。
try:
    open('lebo.txt','r')
except Exception  as result:  # result，是变量，用于存储异常的基本信息
    print("这有个问题", result)

else:
    print('else')
finally:
    print('finally')


# try内由多行代码，如果存在多行代码有问题，当遇到第一个错误的代码的时候就会捕获异常，而不会继续执行下面的代码
try:
    open('lebo.txt','r')
    print(a)  # 不会执行
except Exception  as result:  # result，是变量，用于存储异常的基本信息
    print("这有个问题", result)

else:
    print('else')
finally:
    print('finally')


# try...expect...可以包含在函数或类内，也可以在try内包含函数或类
try:
    def func():
        print(a)

    func()  # 调用函数的时候才可以捕获，而不是定义函数的时候捕获
except:
    print('有问题')



def func():
    print(a)
try:
    func()  # 调用函数的时候才可以捕获，而不是定义函数的时候捕获
except:
    print('有问题')




# 定义属于自己的异常类型
class AgeError(Exception):
    def __init__(self, age):
        self.age = age

    def __str__(self):
        return '0-150age=%d' % self.age

# 使用异常类型，异常业务逻辑
class Person(object):
    def __init__(self,age):
        if age > 0 and age < 150:
            self.age = age
        else:
            # 抛出异常raise
            raise AgeError(age)

xx = Person(5000)
